import java.io.*;
import java.util.*;
import java.text.SimpleDateFormat;

/**
 * 数据管理类 - 负责所有数据的读写操作
 * 
 * 这是整个酒店管理系统的数据访问层（Data Access Layer, DAL）
 * 负责管理所有与文件存储相关的操作
 * 
 * 设计模式：单例模式（Singleton Pattern）
 * 为什么使用单例模式？
 * 1. 全局唯一：整个系统只需要一个数据管理器
 * 2. 资源控制：避免多个实例同时操作文件造成冲突
 * 3. 状态一致：确保所有地方访问的是同一个数据管理器
 * 4. 性能优化：避免重复创建对象
 * 
 * 主要职责：
 * 1. 文件系统管理：创建和维护数据文件
 * 2. 数据持久化：将内存中的对象保存到文件
 * 3. 数据加载：从文件中读取数据并转换为对象
 * 4. 数据验证：确保数据的完整性和正确性
 * 5. 异常处理：处理文件操作中的各种异常情况
 * 
 * 文件结构：
 * data/
 * ├── admin.txt - 管理员账号信息
 * ├── rooms.txt - 房间基本信息
 * ├── guests.txt - 客人信息
 * ├── reservations.txt - 预定记录
 * ├── checkin.txt - 当前入住记录
 * └── history.txt - 历史入住记录
 * 
 * 技术特点：
 * - 使用文本文件存储数据（简单、可读性好）
 * - 采用"|"分隔符格式（便于解析）
 * - 完整的异常处理机制
 * - 线程安全的单例实现
 */
public class DataManager {

    // ==================== 单例模式实现 ====================

    /**
     * 单例实例
     * 
     * static：静态变量，属于类而不是对象
     * volatile：确保多线程环境下的可见性
     * 
     * 为什么使用volatile？
     * 在多线程环境中，如果一个线程修改了instance的值，
     * volatile确保其他线程能立即看到这个变化
     */
    private static volatile DataManager instance;

    // ==================== 文件路径常量 ====================
    // 这些常量定义了所有数据文件的路径
    // 使用常量的好处：
    // 1. 避免硬编码：路径集中管理，便于修改
    // 2. 防止拼写错误：编译时检查
    // 3. 提高可读性：见名知意

    /**
     * 数据目录路径
     * 所有数据文件都存储在这个目录下
     */
    private static final String DATA_DIR = "data/";

    /**
     * 管理员账号文件路径
     * 存储格式：用户名|密码
     * 例如：admin|123456
     */
    private static final String ADMIN_FILE = DATA_DIR + "admin.txt";

    /**
     * 房间信息文件路径
     * 存储格式：房间号|房间类型|价格|状态
     * 例如：101|标准间|200|空闲
     */
    private static final String ROOMS_FILE = DATA_DIR + "rooms.txt";

    /**
     * 客人信息文件路径
     * 存储格式：客人ID|姓名|身份证|电话|地址
     */
    private static final String GUESTS_FILE = DATA_DIR + "guests.txt";

    /**
     * 预定记录文件路径
     * 存储格式：预定号|客人ID|客人姓名|房间号|入住日期|退房日期|总金额|状态
     */
    private static final String RESERVATIONS_FILE = DATA_DIR + "reservations.txt";

    /**
     * 当前入住记录文件路径
     * 存储格式：客人ID|客人姓名|房间号|入住日期|退房日期|总金额|状态
     */
    private static final String CHECK_IN_FILE = DATA_DIR + "checkin.txt";

    /**
     * 历史入住记录文件路径
     * 存储格式：客人ID|客人姓名|房间号|入住日期|退房日期|总金额|状态
     */
    private static final String HISTORY_FILE = DATA_DIR + "history.txt";

    /**
     * 私有构造方法
     * 
     * 单例模式的关键：构造方法必须是私有的
     * 这样外部就不能直接使用new创建对象
     * 只能通过getInstance()方法获取实例
     */
    private DataManager() {
        // 构造方法为空，因为初始化工作在initializeDataFiles()中进行
    }

    /**
     * 获取DataManager的唯一实例
     * 
     * 这是单例模式的核心方法
     * 使用双重检查锁定（Double-Checked Locking）确保线程安全
     * 
     * 为什么要双重检查？
     * 1. 第一次检查：避免不必要的同步开销
     * 2. 同步块：确保只有一个线程能创建实例
     * 3. 第二次检查：防止多个线程同时进入同步块
     * 
     * @return DataManager的唯一实例
     */
    public static DataManager getInstance() {
        // 第一次检查：如果实例已存在，直接返回（避免同步开销）
        if (instance == null) {
            // 同步块：确保只有一个线程能执行这段代码
            synchronized (DataManager.class) {
                // 第二次检查：防止多个线程同时通过第一次检查
                if (instance == null) {
                    // 创建唯一实例
                    instance = new DataManager();
                }
            }
        }
        return instance;
    }

    /**
     * 初始化数据文件
     * 
     * 这个方法在系统启动时调用，负责：
     * 1. 创建数据目录（如果不存在）
     * 2. 初始化各种数据文件
     * 3. 设置默认数据
     * 
     * 异常处理：
     * 使用try-catch捕获可能的IO异常
     * 如果初始化失败，会打印错误信息
     */
    public void initializeDataFiles() {
        try {
            // ==================== 创建数据目录 ====================

            // 创建File对象表示数据目录
            File dataDir = new File(DATA_DIR);

            // 检查目录是否存在，如果不存在则创建
            if (!dataDir.exists()) {
                // mkdirs()方法会创建目录及其父目录
                // 返回值表示是否创建成功
                boolean created = dataDir.mkdirs();
                if (created) {
                    System.out.println("数据目录创建成功：" + DATA_DIR);
                } else {
                    System.err.println("数据目录创建失败：" + DATA_DIR);
                }
            }

            // ==================== 初始化各种数据文件 ====================

            // 初始化管理员文件（包含默认账号）
            initializeAdminFile();

            // 初始化房间文件（包含默认房间）
            initializeRoomsFile();

            // 初始化其他文件（创建空文件）
            createFileIfNotExists(GUESTS_FILE);
            createFileIfNotExists(RESERVATIONS_FILE);
            createFileIfNotExists(CHECK_IN_FILE);
            createFileIfNotExists(HISTORY_FILE);

        } catch (Exception e) {
            // 捕获所有异常，防止程序崩溃
            System.err.println("初始化数据文件时发生错误：" + e.getMessage());
            e.printStackTrace(); // 打印详细的错误堆栈
        }
    }

    /**
     * 初始化管理员文件
     * 
     * 如果管理员文件不存在，创建一个默认的管理员账号
     * 默认账号：admin/123456
     * 
     * @throws IOException 如果文件操作失败
     */
    private void initializeAdminFile() throws IOException {
        File adminFile = new File(ADMIN_FILE);

        // 检查文件是否存在
        if (!adminFile.exists()) {
            // 使用try-with-resources语句自动关闭资源
            // 这是Java 7引入的特性，确保资源被正确关闭
            try (PrintWriter writer = new PrintWriter(new FileWriter(adminFile))) {
                // 写入默认管理员账号
                // 格式：用户名|密码
                writer.println("admin|123456");
                System.out.println("默认管理员账号已创建：admin/123456");
            }
            // try-with-resources会自动调用writer.close()
        }
    }

    /**
     * 初始化房间文件
     * 
     * 如果房间文件不存在，创建默认的房间数据
     * 房间配置：
     * - 标准间：101-110，200元/晚
     * - 豪华间：201-210，350元/晚
     * - 套房：301-305，500元/晚
     * 
     * @throws IOException 如果文件操作失败
     */
    private void initializeRoomsFile() throws IOException {
        File roomsFile = new File(ROOMS_FILE);

        if (!roomsFile.exists()) {
            try (PrintWriter writer = new PrintWriter(
                    new OutputStreamWriter(new FileOutputStream(roomsFile), "UTF-8"))) {
                // 创建标准间：101-110
                for (int i = 101; i <= 110; i++) {
                    // 格式：房间号|房间类型|价格|状态
                    writer.println(i + "|标准间|200|空闲");
                }

                // 创建豪华间：201-210
                for (int i = 201; i <= 210; i++) {
                    writer.println(i + "|豪华间|350|空闲");
                }

                // 创建套房：301-305
                for (int i = 301; i <= 305; i++) {
                    writer.println(i + "|套房|500|空闲");
                }

                System.out.println("默认房间数据已创建：25间房间");
            }
        }
    }

    /**
     * 创建文件（如果不存在）
     * 
     * 这是一个工具方法，用于创建空的数据文件
     * 
     * @param filename 要创建的文件路径
     * @throws IOException 如果文件创建失败
     */
    private void createFileIfNotExists(String filename) throws IOException {
        File file = new File(filename);
        if (!file.exists()) {
            // createNewFile()方法创建一个空文件
            boolean created = file.createNewFile();
            if (created) {
                System.out.println("文件已创建：" + filename);
            }
        }
    }

    // ==================== 管理员账号管理 ====================

    /**
     * 验证管理员登录
     * 
     * 这个方法检查用户输入的用户名和密码是否正确
     * 
     * 工作流程：
     * 1. 打开管理员文件
     * 2. 逐行读取账号信息
     * 3. 分割每行数据（用户名|密码）
     * 4. 比较用户名和密码
     * 5. 返回验证结果
     * 
     * @param username 用户输入的用户名
     * @param password 用户输入的密码
     * @return true表示验证成功，false表示验证失败
     */
    public boolean validateAdmin(String username, String password) {
        // 使用try-with-resources确保文件资源被正确关闭
        try (BufferedReader reader = new BufferedReader(new FileReader(ADMIN_FILE))) {
            String line;

            // 逐行读取文件内容
            // readLine()返回null表示文件结束
            while ((line = reader.readLine()) != null) {
                // 分割每行数据：用户名|密码
                String[] parts = line.split("\\|");

                // 检查数据格式是否正确（应该有2个部分）
                if (parts.length == 2) {
                    // parts[0]是用户名，parts[1]是密码
                    if (parts[0].equals(username) && parts[1].equals(password)) {
                        return true; // 验证成功
                    }
                }
            }
        } catch (IOException e) {
            // 处理文件读取异常
            System.err.println("读取管理员文件时发生错误：" + e.getMessage());
            e.printStackTrace();
        }

        return false; // 验证失败
    }

    /**
     * 修改管理员密码
     * 
     * 这个方法允许管理员修改自己的密码
     * 
     * 工作流程：
     * 1. 验证原密码是否正确
     * 2. 读取所有账号信息到内存
     * 3. 修改指定账号的密码
     * 4. 将修改后的数据写回文件
     * 
     * 为什么要先读取所有数据？
     * 因为文件不支持直接修改某一行，需要重写整个文件
     * 
     * @param username    用户名
     * @param oldPassword 原密码
     * @param newPassword 新密码
     * @return true表示修改成功，false表示修改失败
     */
    public boolean changeAdminPassword(String username, String oldPassword, String newPassword) {
        // 第一步：验证原密码
        if (!validateAdmin(username, oldPassword)) {
            return false; // 原密码错误
        }

        try {
            // 第二步：读取所有账号信息
            List<String> lines = new ArrayList<>();

            try (BufferedReader reader = new BufferedReader(new FileReader(ADMIN_FILE))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    String[] parts = line.split("\\|");

                    // 如果是要修改的账号，更新密码
                    if (parts.length == 2 && parts[0].equals(username)) {
                        lines.add(username + "|" + newPassword);
                    } else {
                        // 其他账号保持不变
                        lines.add(line);
                    }
                }
            }

            // 第三步：将修改后的数据写回文件
            try (PrintWriter writer = new PrintWriter(new FileWriter(ADMIN_FILE))) {
                for (String line : lines) {
                    writer.println(line);
                }
            }

            System.out.println("管理员密码修改成功");
            return true;

        } catch (IOException e) {
            System.err.println("修改管理员密码时发生错误：" + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    // ==================== 房间信息管理 ====================

    /**
     * 获取所有房间信息
     * 
     * 这个方法从文件中读取所有房间数据，并转换为Room对象列表
     * 
     * 工作流程：
     * 1. 创建空的房间列表
     * 2. 打开房间文件
     * 3. 逐行读取房间数据
     * 4. 将每行数据转换为Room对象
     * 5. 添加到列表中
     * 6. 返回完整列表
     * 
     * @return 包含所有房间信息的列表
     */
    public List<Room> getAllRooms() {
        // 创建ArrayList存储房间对象
        // ArrayList是List接口的实现，支持动态数组
        List<Room> rooms = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new FileReader(ROOMS_FILE))) {
            String line;

            while ((line = reader.readLine()) != null) {
                // 分割房间数据：房间号|房间类型|价格|状态
                String[] parts = line.split("\\|");

                // 验证数据格式
                if (parts.length == 4) {
                    // 创建Room对象
                    // parts[2]是价格，需要转换为double类型
                    Room room = new Room(parts[0], parts[1], Double.parseDouble(parts[2]), parts[3]);
                    rooms.add(room);
                }
            }
        } catch (IOException e) {
            System.err.println("读取房间文件时发生错误：" + e.getMessage());
            e.printStackTrace();
        } catch (NumberFormatException e) {
            System.err.println("房间价格格式错误：" + e.getMessage());
            e.printStackTrace();
        }

        return rooms;
    }

    /**
     * 更新房间状态
     * 
     * 这个方法用于修改指定房间的状态
     * 常用场景：
     * - 客人入住：空闲 -> 已入住
     * - 客人退房：已入住 -> 空闲
     * - 房间预定：空闲 -> 已预定
     * 
     * @param roomNumber 房间号
     * @param status     新的状态
     * @return true表示更新成功，false表示更新失败
     */
    public boolean updateRoomStatus(String roomNumber, String status) {
        try {
            // 读取所有房间信息
            List<String> lines = new ArrayList<>();

            try (BufferedReader reader = new BufferedReader(new FileReader(ROOMS_FILE))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    String[] parts = line.split("\\|");

                    // 如果是要更新的房间，修改状态
                    if (parts.length == 4 && parts[0].equals(roomNumber)) {
                        // 重新组装房间数据，只修改状态部分
                        lines.add(parts[0] + "|" + parts[1] + "|" + parts[2] + "|" + status);
                    } else {
                        // 其他房间保持不变
                        lines.add(line);
                    }
                }
            }

            // 写回文件
            try (PrintWriter writer = new PrintWriter(new FileWriter(ROOMS_FILE))) {
                for (String line : lines) {
                    writer.println(line);
                }
            }

            System.out.println("房间" + roomNumber + "状态已更新为：" + status);
            return true;

        } catch (IOException e) {
            System.err.println("更新房间状态时发生错误：" + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    // ==================== 客人信息管理 ====================

    /**
     * 添加客人信息
     * 
     * 将新的客人信息追加到客人文件中
     * 
     * @param guest 要添加的客人对象
     * @return true表示添加成功，false表示添加失败
     */
    public boolean addGuest(Guest guest) {
        // 使用FileWriter的追加模式（第二个参数为true）
        try (PrintWriter writer = new PrintWriter(new FileWriter(GUESTS_FILE, true))) {
            // 调用Guest对象的toFileString()方法获取文件格式的字符串
            writer.println(guest.toFileString());
            System.out.println("客人信息已添加：" + guest.getName());
            return true;
        } catch (IOException e) {
            System.err.println("添加客人信息时发生错误：" + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取所有客人信息
     * 
     * @return 包含所有客人信息的列表
     */
    public List<Guest> getAllGuests() {
        List<Guest> guests = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new FileReader(GUESTS_FILE))) {
            String line;

            while ((line = reader.readLine()) != null) {
                // 使用Guest类的静态方法从字符串创建对象
                Guest guest = Guest.fromFileString(line);

                // 检查对象是否创建成功
                if (guest != null) {
                    guests.add(guest);
                }
            }
        } catch (IOException e) {
            System.err.println("读取客人文件时发生错误：" + e.getMessage());
            e.printStackTrace();
        }

        return guests;
    }

    // ==================== 预定记录管理 ====================

    /**
     * 添加预定记录
     * 
     * @param reservation 要添加的预定对象
     * @return true表示添加成功，false表示添加失败
     */
    public boolean addReservation(Reservation reservation) {
        try (PrintWriter writer = new PrintWriter(new FileWriter(RESERVATIONS_FILE, true))) {
            writer.println(reservation.toFileString());
            System.out.println("预定记录已添加：" + reservation.getReservationId());
            return true;
        } catch (IOException e) {
            System.err.println("添加预定记录时发生错误：" + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取所有预定记录
     * 
     * @return 包含所有预定记录的列表
     */
    public List<Reservation> getAllReservations() {
        List<Reservation> reservations = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new FileReader(RESERVATIONS_FILE))) {
            String line;

            while ((line = reader.readLine()) != null) {
                Reservation reservation = Reservation.fromFileString(line);
                if (reservation != null) {
                    reservations.add(reservation);
                }
            }
        } catch (IOException e) {
            System.err.println("读取预定文件时发生错误：" + e.getMessage());
            e.printStackTrace();
        }

        return reservations;
    }

    // ==================== 入住记录管理 ====================

    /**
     * 添加入住记录
     * 
     * @param checkIn 要添加的入住记录对象
     * @return true表示添加成功，false表示添加失败
     */
    public boolean addCheckIn(CheckIn checkIn) {
        try (PrintWriter writer = new PrintWriter(new FileWriter(CHECK_IN_FILE, true))) {
            writer.println(checkIn.toFileString());
            System.out.println("入住记录已添加：" + checkIn.getGuestName());
            return true;
        } catch (IOException e) {
            System.err.println("添加入住记录时发生错误：" + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取所有入住记录
     * 
     * @return 包含所有当前入住记录的列表
     */
    public List<CheckIn> getAllCheckIns() {
        List<CheckIn> checkIns = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new FileReader(CHECK_IN_FILE))) {
            String line;

            while ((line = reader.readLine()) != null) {
                CheckIn checkIn = CheckIn.fromFileString(line);
                if (checkIn != null) {
                    checkIns.add(checkIn);
                }
            }
        } catch (IOException e) {
            System.err.println("读取入住文件时发生错误：" + e.getMessage());
            e.printStackTrace();
        }

        return checkIns;
    }

    // ==================== 历史记录管理 ====================

    /**
     * 添加历史记录
     * 
     * 当客人结账时，将入住记录移动到历史记录文件
     * 
     * @param checkIn 要添加到历史记录的入住记录
     * @return true表示添加成功，false表示添加失败
     */
    public boolean addHistory(CheckIn checkIn) {
        try (PrintWriter writer = new PrintWriter(new FileWriter(HISTORY_FILE, true))) {
            writer.println(checkIn.toFileString());
            System.out.println("历史记录已添加：" + checkIn.getGuestName());
            return true;
        } catch (IOException e) {
            System.err.println("添加历史记录时发生错误：" + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取所有历史记录
     * 
     * @return 包含所有历史入住记录的列表
     */
    public List<CheckIn> getAllHistory() {
        List<CheckIn> history = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new FileReader(HISTORY_FILE))) {
            String line;

            while ((line = reader.readLine()) != null) {
                CheckIn checkIn = CheckIn.fromFileString(line);
                if (checkIn != null) {
                    history.add(checkIn);
                }
            }
        } catch (IOException e) {
            System.err.println("读取历史文件时发生错误：" + e.getMessage());
            e.printStackTrace();
        }

        return history;
    }

    /**
     * 删除入住记录（结账时使用）
     * 
     * 当客人结账时，需要从当前入住记录中删除该记录
     * 同时将记录添加到历史记录中
     * 
     * 工作流程：
     * 1. 读取所有入住记录
     * 2. 过滤掉要删除的记录
     * 3. 将剩余记录写回文件
     * 
     * @param guestId 要删除的客人ID
     * @return true表示删除成功，false表示删除失败
     */
    public boolean removeCheckIn(String guestId) {
        try {
            List<String> lines = new ArrayList<>();

            // 读取所有记录，过滤掉要删除的记录
            try (BufferedReader reader = new BufferedReader(new FileReader(CHECK_IN_FILE))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    String[] parts = line.split("\\|");

                    // 如果不是要删除的记录，保留
                    if (parts.length > 0 && !parts[0].equals(guestId)) {
                        lines.add(line);
                    }
                }
            }

            // 将过滤后的记录写回文件
            try (PrintWriter writer = new PrintWriter(new FileWriter(CHECK_IN_FILE))) {
                for (String line : lines) {
                    writer.println(line);
                }
            }

            System.out.println("入住记录已删除：客人ID " + guestId);
            return true;

        } catch (IOException e) {
            System.err.println("删除入住记录时发生错误：" + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
}